/*
 * Copyright (C) 2011, EADS France
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */

package xowl.interpreter.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 *
 * @author Laurent WOUTERS
 */
public abstract class BaseRepository extends BaseIRIProvider implements xowl.interpreter.Repository {
    static final String comp = "XOWL Repo";
    
    protected xowl.utils.logging.Logger log;
    protected AggregatedEvaluator defaultEvaluator;
    protected ThreadRunner defaultThread;
    protected List<Evaluator> evaluatorParts;
    protected Map<String, xowl.lang.owl2.Ontology> mapOntologies;
    protected Map<xowl.lang.owl2.Ontology, Map<String, xowl.lang.runtime.Entity>> mapEntities;
    protected Map<xowl.lang.actions.Lambda, xowl.lang.runtime.Function> mapFunctions;
    protected Map<xowl.lang.runtime.Class, List<xowl.interpreter.observers.ClassObserver>> mapObsClass;
    protected Map<xowl.lang.runtime.ObjectProperty, Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.ObjectPropertyObserver>>> mapObsObjProp;
    protected Map<xowl.lang.runtime.DataProperty, Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.DataPropertyObserver>>> mapObsDataProp;
    protected xowl.interpreter.Transaction transaction;
    
    // Internal events for specialized repositories
    protected void onNewOntology(xowl.lang.owl2.Ontology ontology) { }
    protected void onNewEntity(xowl.lang.owl2.Ontology ontology, xowl.lang.runtime.Entity entity) { }
    protected void onNewInterpretationAsClass(xowl.lang.runtime.Entity entity, xowl.lang.runtime.Class interpretation) { }
    protected void onNewInterpretationAsIndividual(xowl.lang.runtime.Entity entity, xowl.lang.runtime.NamedIndividual interpretation) { }
    protected void onNewInterpretationAsObjectProperty(xowl.lang.runtime.Entity entity, xowl.lang.runtime.ObjectProperty interpretation) { }
    protected void onNewInterpretationAsDataProperty(xowl.lang.runtime.Entity entity, xowl.lang.runtime.DataProperty interpretation) { }
    protected void onNewInterpretationAsDatatype(xowl.lang.runtime.Entity entity, xowl.lang.runtime.Datatype interpretation) { }
    protected void onNewInterpretationAsAnnotationProperty(xowl.lang.runtime.Entity entity, xowl.lang.runtime.AnnotationProperty interpretation) { }
    protected void onNewInterpretationAsFunction(xowl.lang.runtime.Entity entity, xowl.lang.runtime.Function interpretation) { }
    
    public xowl.utils.logging.Logger getLog() { return log; }
    public AggregatedEvaluator getDefaultEvaluator() { return defaultEvaluator; }
    public Collection<Evaluator> getEvaluatorParts() { return evaluatorParts; }
    
    public BaseRepository(xowl.utils.logging.Logger log) {
        this.log = log;
        evaluatorParts = new ArrayList<Evaluator>();
        mapOntologies = new HashMap<String, xowl.lang.owl2.Ontology>();
        mapEntities = new HashMap<xowl.lang.owl2.Ontology, Map<String, xowl.lang.runtime.Entity>>();
        mapFunctions = new HashMap<xowl.lang.actions.Lambda, xowl.lang.runtime.Function>();
        mapObsClass = new HashMap<xowl.lang.runtime.Class, List<xowl.interpreter.observers.ClassObserver>>();
        mapObsObjProp = new HashMap<xowl.lang.runtime.ObjectProperty, Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.ObjectPropertyObserver>>>();
        mapObsDataProp = new HashMap<xowl.lang.runtime.DataProperty, Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.DataPropertyObserver>>>();
        defaultEvaluator = new AggregatedEvaluator(this);
        defaultThread = new ThreadRunner(this, defaultEvaluator);
    }
    
    protected abstract void apply(AggregatedEvaluator evaluator, xowl.interpreter.Change change) throws xowl.interpreter.XOWLException;
    protected abstract void apply(AggregatedEvaluator evaluator, xowl.interpreter.Changeset changeset) throws xowl.interpreter.XOWLException;
    protected abstract void apply(AggregatedEvaluator evaluator, xowl.interpreter.RuleSet ruleset);
    protected abstract void unapply(AggregatedEvaluator evaluator, xowl.interpreter.RuleSet ruleset);
    protected abstract void apply(AggregatedEvaluator evaluator, xowl.interpreter.Transformation transfo);
    protected abstract void unapply(AggregatedEvaluator evaluator, xowl.interpreter.Transformation transfo);
    
    @Override public xowl.lang.owl2.Ontology getOntology(String uri) { return mapOntologies.get(uri); }
    @Override public xowl.lang.owl2.Ontology getOntology(xowl.lang.owl2.IRI iri) { return mapOntologies.get(iri.getHasValue()); }
    @Override public Collection<xowl.lang.owl2.Ontology> getOntologies() { return mapOntologies.values(); }
    @Override public xowl.lang.owl2.Ontology resolveOntology(String iri) {
        if (mapOntologies.containsKey(iri))
            return mapOntologies.get(iri);
        xowl.lang.owl2.Ontology ontology = new xowl.lang.owl2.Ontology();
        ontology.setHasIRI(getIRI(iri));
        mapOntologies.put(iri, ontology);
        mapEntities.put(ontology, new HashMap<String, xowl.lang.runtime.Entity>());
        onNewOntology(ontology);
        return ontology;
    }
    @Override public xowl.lang.owl2.Ontology resolveOntology(xowl.lang.owl2.IRI iri) {
        if (mapOntologies.containsKey(iri.getHasValue()))
            return mapOntologies.get(iri.getHasValue());
        xowl.lang.owl2.Ontology ontology = new xowl.lang.owl2.Ontology();
        ontology.setHasIRI(iri);
        mapOntologies.put(iri.getHasValue(), ontology);
        mapEntities.put(ontology, new HashMap<String, xowl.lang.runtime.Entity>());
        onNewOntology(ontology);
        return ontology;
    }
    
    @Override public xowl.lang.runtime.Value getLiteral(Object data) {
        xowl.lang.runtime.Literal literal = new xowl.lang.runtime.Literal();
        literal.setLexicalValue(data.toString());
        java.lang.Class c = data.getClass();
        if (c == java.lang.String.class)
            literal.setMemberOf(interpretAsDatatype(resolveEntity(OWLDatatype.xsdString)));
        else if (c == java.lang.Boolean.TYPE || c == java.lang.Boolean.class)
            literal.setMemberOf(interpretAsDatatype(resolveEntity(OWLDatatype.xsdBoolean)));
        else if (c == java.lang.Integer.TYPE || c == java.lang.Integer.class)
            literal.setMemberOf(interpretAsDatatype(resolveEntity(OWLDatatype.xsdInteger)));
        else if (c == java.lang.Float.TYPE || c == java.lang.Float.class)
            literal.setMemberOf(interpretAsDatatype(resolveEntity(OWLDatatype.xsdFloat)));
        return literal;
    }
    @Override public xowl.lang.runtime.Entity resolveEntity(String iri) {
        return resolveEntity(iri, null);
    }
    @Override public xowl.lang.runtime.Entity resolveEntity(xowl.lang.owl2.IRI iri) {
        return resolveEntity(iri.getHasValue(), iri);
    }
    private xowl.lang.runtime.Entity resolveEntity(String value, xowl.lang.owl2.IRI iri) {
        String[] parts = value.split("#");
        xowl.lang.owl2.Ontology ontology = null;
        String name = null;
        Map<String, xowl.lang.runtime.Entity> sub = null;
        if (parts.length > 1) {
            ontology = resolveOntology(parts[0]);
            name = parts[1];
            sub = mapEntities.get(ontology);
        } else {
            sub = mapEntities.get(null);
            if (sub == null) {
                sub = new HashMap<String, xowl.lang.runtime.Entity>();
                mapEntities.put(null, sub);
            }
            name = value;
        }
        
        xowl.lang.runtime.Entity entity = sub.get(name);
        if (entity != null)
            return entity;
        
        if (iri == null)
            iri = getIRI(value);
        entity = new xowl.lang.runtime.Entity();
        entity.setHasIRI(iri);
        
        if (ontology != null)
            ontology.addContains(entity);
        sub.put(name, entity);
        onNewEntity(ontology, entity);
        return entity;
    }
    
    @Override public xowl.lang.runtime.Class interpretAsClass(xowl.lang.runtime.Entity entity) {
        for (xowl.lang.runtime.Interpretation interpretation : entity.getAllInterpretedAs()) {
            if (interpretation instanceof xowl.lang.runtime.Class)
                return (xowl.lang.runtime.Class)interpretation;
        }
        xowl.lang.runtime.Class interpretation = new xowl.lang.runtime.Class();
        entity.addInterpretedAs(interpretation);
        onNewInterpretationAsClass(entity, interpretation);
        return interpretation;
    }
    @Override public xowl.lang.runtime.NamedIndividual interpretAsIndividual(xowl.lang.runtime.Entity entity) {
        for (xowl.lang.runtime.Interpretation interpretation : entity.getAllInterpretedAs()) {
            if (interpretation instanceof xowl.lang.runtime.NamedIndividual)
                return (xowl.lang.runtime.NamedIndividual)interpretation;
        }
        xowl.lang.runtime.NamedIndividual interpretation = new xowl.lang.runtime.NamedIndividual();
        entity.addInterpretedAs(interpretation);
        onNewInterpretationAsIndividual(entity, interpretation);
        return interpretation;
    }
    @Override public xowl.lang.runtime.ObjectProperty interpretAsObjectProperty(xowl.lang.runtime.Entity entity) {
        for (xowl.lang.runtime.Interpretation interpretation : entity.getAllInterpretedAs()) {
            if (interpretation instanceof xowl.lang.runtime.ObjectProperty)
                return (xowl.lang.runtime.ObjectProperty)interpretation;
        }
        xowl.lang.runtime.ObjectProperty interpretation = new xowl.lang.runtime.ObjectProperty();
        entity.addInterpretedAs(interpretation);
        onNewInterpretationAsObjectProperty(entity, interpretation);
        return interpretation;
    }
    @Override public xowl.lang.runtime.DataProperty interpretAsDataProperty(xowl.lang.runtime.Entity entity) {
        for (xowl.lang.runtime.Interpretation interpretation : entity.getAllInterpretedAs()) {
            if (interpretation instanceof xowl.lang.runtime.DataProperty)
                return (xowl.lang.runtime.DataProperty)interpretation;
        }
        xowl.lang.runtime.DataProperty interpretation = new xowl.lang.runtime.DataProperty();
        entity.addInterpretedAs(interpretation);
        onNewInterpretationAsDataProperty(entity, interpretation);
        return interpretation;
    }
    @Override public xowl.lang.runtime.Datatype interpretAsDatatype(xowl.lang.runtime.Entity entity) {
        for (xowl.lang.runtime.Interpretation interpretation : entity.getAllInterpretedAs()) {
            if (interpretation instanceof xowl.lang.runtime.Datatype)
                return (xowl.lang.runtime.Datatype)interpretation;
        }
        xowl.lang.runtime.Datatype interpretation = new xowl.lang.runtime.Datatype();
        entity.addInterpretedAs(interpretation);
        onNewInterpretationAsDatatype(entity, interpretation);
        return interpretation;
    }
    @Override public xowl.lang.runtime.AnnotationProperty interpreterAsAnnotationProperty(xowl.lang.runtime.Entity entity) {
        for (xowl.lang.runtime.Interpretation interpretation : entity.getAllInterpretedAs()) {
            if (interpretation instanceof xowl.lang.runtime.AnnotationProperty)
                return (xowl.lang.runtime.AnnotationProperty)interpretation;
        }
        xowl.lang.runtime.AnnotationProperty interpretation = new xowl.lang.runtime.AnnotationProperty();
        entity.addInterpretedAs(interpretation);
        onNewInterpretationAsAnnotationProperty(entity, interpretation);
        return interpretation;
    }
    @Override public xowl.lang.runtime.Function interpretAsFunction(xowl.lang.runtime.Entity entity) {
        for (xowl.lang.runtime.Interpretation interpretation : entity.getAllInterpretedAs()) {
            if (interpretation instanceof xowl.lang.runtime.Function)
                return (xowl.lang.runtime.Function)interpretation;
        }
        xowl.lang.runtime.Function interpretation = new xowl.lang.runtime.Function();
        entity.addInterpretedAs(interpretation);
        onNewInterpretationAsFunction(entity, interpretation);
        return interpretation;
    }
    
    @Override public Collection<xowl.interpreter.QuerySolution> select(xowl.lang.actions.Select query) throws xowl.interpreter.XOWLException {
        return defaultEvaluator.select(defaultEvaluator, query);
    }
    
    @Override public void apply(xowl.interpreter.Change change) throws xowl.interpreter.XOWLException { apply(defaultEvaluator, change); }
    @Override public void apply(xowl.interpreter.Changeset changeset) throws xowl.interpreter.XOWLException { apply(defaultEvaluator, changeset); }
    @Override public void apply(xowl.interpreter.RuleSet ruleset) { apply(defaultEvaluator, ruleset); }
    @Override public void unapply(xowl.interpreter.RuleSet ruleset) { unapply(defaultEvaluator, ruleset); }
    @Override public void apply(xowl.interpreter.Transformation transfo) { apply(defaultEvaluator, transfo); }
    @Override public void unapply(xowl.interpreter.Transformation transfo) { unapply(defaultEvaluator, transfo); }
    
    @Override public xowl.lang.owl2.Ontology loadOntology(String file, xowl.interpreter.OWL2Syntax type) {
        try {
            java.io.Reader reader = xowl.utils.Files.getReader(file);
            xowl.lang.owl2.Ontology result = loadOntology(file, reader, type);
            reader.close();
            return result;
        } catch (java.io.FileNotFoundException ex) {
            log.error(comp, "Cannot find file " + file);
        } catch (java.io.IOException ex) {
            log.error(comp, "Error closing the file " + file + ": " + ex.getMessage());
        }
        return null;
    }
    @Override public xowl.lang.owl2.Ontology loadOntology(final String name, java.io.Reader reader, xowl.interpreter.OWL2Syntax type) {
        log.info(comp, "Loading [" + type.toString() + "] " + name + " ... ");
        xowl.interpreter.frontends.Loader loader = null;
        switch (type) {
            case OWLXML: loader = new xowl.interpreter.frontends.owlxml.OWLXMLLoader(); break;
            case Functional: loader = new xowl.interpreter.frontends.functional.FunctionalLoader(); break;
            case RDFXML: loader = new xowl.interpreter.frontends.rdfxml.RDFXMLLoader(); break;
            case NTriples: loader = new xowl.interpreter.frontends.ntriples.NTriplesLoader(); break;
        }
        final xowl.interpreter.Changeset changeset = new xowl.interpreter.Changeset();
        final List<xowl.lang.rules.Rule> rules = new ArrayList<xowl.lang.rules.Rule>();
        xowl.lang.owl2.Ontology onto = loader.loadOntology(log, name, reader, new xowl.interpreter.frontends.OntologyListener() {
            @Override public xowl.lang.owl2.Ontology resolveOntology(xowl.lang.owl2.IRI iri) { return BaseRepository.this.resolveOntology(iri); }
            @Override public xowl.lang.runtime.Datatype getDatatype(String iri) { return interpretAsDatatype(resolveEntity(iri)); }
            @Override public void onRule(xowl.lang.owl2.Ontology onto, xowl.lang.rules.Rule rule) { rules.add(rule); }
            @Override public void onAxiom(xowl.lang.owl2.Ontology onto, xowl.lang.owl2.Axiom axiom) { changeset.add(new xowl.interpreter.Change(onto, axiom, true)); }
            @Override public xowl.lang.owl2.IRI getIRI(String fullIri) { return BaseRepository.this.getIRI(fullIri); }
        });
        try {
            apply(defaultEvaluator, changeset);
            apply(defaultEvaluator, new xowl.interpreter.RuleSet(onto, rules));
        }
        catch(xowl.interpreter.XOWLException ex) {
            log.error(comp, "Failed to apply changeset!");
            log.error(comp, ex.getHeadline(), ex);
        }
        if (onto != null) log.info(comp, "done");
        else log.error(comp, "failed!");
        return onto;
    }
    @Override public xowl.lang.owl2.Ontology loadOntology(String name, java.io.InputStream stream, xowl.interpreter.OWL2Syntax type) {
        try {
            return loadOntology(name, new java.io.InputStreamReader(stream, "UTF-8"), type);
        } catch (java.io.UnsupportedEncodingException ex) {
            log.error(comp, "Unsupported encoding: UTF-8", ex);
            return null;
        }
    }
    
    @Override public boolean writeOntology(xowl.lang.owl2.Ontology ontology, String file, xowl.interpreter.OWL2Syntax type) {
        try {
            java.io.Writer writer = xowl.utils.Files.getWriter(file);
            boolean result = writeOntology(ontology, writer, type);
            writer.close();
            return result;
        } catch (java.io.IOException ex) {
            log.error(comp, "Error opening the file " + file + ": " + ex.getMessage());
        }
        return false;
    }
    @Override public boolean writeOntology(xowl.lang.owl2.Ontology ontology, java.io.Writer writer, xowl.interpreter.OWL2Syntax type) {
        log.info(comp, "Writing [" + type.toString() + "] " + ontology.getHasIRI().getHasValue() + " ... ");
        xowl.interpreter.frontends.Writer ontoWriter = null;
        switch (type) {
            case Functional: ontoWriter = new xowl.interpreter.frontends.functional.FunctionalWriter(); break;
        }
        Collection<xowl.lang.owl2.Axiom> axioms = getAxioms(ontology);
        try { ontoWriter.write(writer, ontology, axioms); }
        catch (java.io.IOException ex) {
            log.error(comp, "Error writing: " + ex.getMessage());
            return false;
        }
        log.info(comp, "done");
        return true;
    }

    @Override public xowl.interpreter.RuleSet loadRules(String file, xowl.interpreter.OWL2Syntax type) {
        try {
            java.io.Reader reader = xowl.utils.Files.getReader(file);
            xowl.interpreter.RuleSet result = loadRules(file, reader, type);
            reader.close();
            return result;
        } catch (java.io.FileNotFoundException ex) {
            log.error(comp, "Cannot find file " + file);
        } catch (java.io.IOException ex) {
            log.error(comp, "Error closing the file " + file + ": " + ex.getMessage());
        }
        return null;
    }
    @Override public xowl.interpreter.RuleSet loadRules(final String name, java.io.Reader reader, xowl.interpreter.OWL2Syntax type) {
        log.info(comp, "Loading [" + type.toString() + "] " + name + " ... ");
        xowl.interpreter.frontends.Loader loader = null;
        switch (type) {
            case OWLXML: loader = new xowl.interpreter.frontends.owlxml.OWLXMLLoader(); break;
            case Functional: loader = new xowl.interpreter.frontends.functional.FunctionalLoader(); break;
        }
        final List<xowl.lang.rules.Rule> rules = new ArrayList<xowl.lang.rules.Rule>();
        xowl.lang.owl2.Ontology onto = loader.loadOntology(log, name, reader, new xowl.interpreter.frontends.OntologyListener() {
            @Override public xowl.lang.owl2.Ontology resolveOntology(xowl.lang.owl2.IRI iri) { return BaseRepository.this.resolveOntology(iri); }
            @Override public xowl.lang.runtime.Datatype getDatatype(String iri) { return interpretAsDatatype(resolveEntity(iri)); }
            @Override public void onRule(xowl.lang.owl2.Ontology onto, xowl.lang.rules.Rule rule) { rules.add(rule); }
            @Override public void onAxiom(xowl.lang.owl2.Ontology onto, xowl.lang.owl2.Axiom axiom) { }
            @Override public xowl.lang.owl2.IRI getIRI(String fullIri) { return BaseRepository.this.getIRI(fullIri); }
        });
        if (onto != null) log.info(comp, "done");
        else log.error(comp, "failed!");
        return new xowl.interpreter.RuleSet(onto, rules);
    }
    @Override public xowl.interpreter.RuleSet loadRules(String name, java.io.InputStream stream, xowl.interpreter.OWL2Syntax type) {
        try {
            return loadRules(name, new java.io.InputStreamReader(stream, "UTF-8"), type);
        } catch (java.io.UnsupportedEncodingException ex) {
            return null;
        }
    }
    
    @Override public List<xowl.lang.actions.Select> loadQueries(String file, xowl.interpreter.OWL2Syntax type) {
        try {
            java.io.Reader reader = xowl.utils.Files.getReader(file);
            List<xowl.lang.actions.Select> result = loadQueries(file, reader, type);
            reader.close();
            return result;
        } catch (java.io.FileNotFoundException ex) {
            log.error(comp, "Cannot find file " + file);
        } catch (java.io.IOException ex) {
            log.error(comp, "Error closing the file " + file + ": " + ex.getMessage());
        }
        return null;
    }
    @Override public List<xowl.lang.actions.Select> loadQueries(String name, java.io.Reader reader, xowl.interpreter.OWL2Syntax type) {
        log.info(comp, "Loading [" + type.toString() + "] " + name + " ... ");
        xowl.interpreter.frontends.Loader loader = null;
        switch (type) {
            case OWLXML: loader = new xowl.interpreter.frontends.owlxml.OWLXMLLoader(); break;
            case Functional: loader = new xowl.interpreter.frontends.functional.FunctionalLoader(); break;
        }
        final List<xowl.lang.actions.Select> queries = new ArrayList<xowl.lang.actions.Select>();
        loader.loadQueries(log, name, reader, new xowl.interpreter.frontends.QueriesListener() {
            @Override public xowl.lang.owl2.Ontology resolveOntology(xowl.lang.owl2.IRI iri) { return BaseRepository.this.resolveOntology(iri); }
            @Override public xowl.lang.runtime.Datatype getDatatype(String iri) { return interpretAsDatatype(resolveEntity(iri)); }
            @Override public xowl.lang.owl2.IRI getIRI(String fullIri) { return BaseRepository.this.getIRI(fullIri); }
            @Override public void onSelect(xowl.lang.actions.Select select) { queries.add(select); }
        });
        log.info(comp, "done");
        return queries;
    }
    @Override public List<xowl.lang.actions.Select> loadQueries(String name, java.io.InputStream stream, xowl.interpreter.OWL2Syntax type) {
        try {
            return loadQueries(name, new java.io.InputStreamReader(stream, "UTF-8"), type);
        } catch (java.io.UnsupportedEncodingException ex) {
            log.error(comp, "Unsupported encoding: UTF-8", ex);
            return null;
        }
    }
    
    @Override public void beginTransaction(String name) {
        if (transaction != null)
            return;
        transaction = new xowl.interpreter.Transaction(name);
    }
    @Override public xowl.interpreter.Transaction endTransaction() {
        xowl.interpreter.Transaction value = transaction;
        transaction = null;
        return value;
    }

    @Override public void addObserver(xowl.interpreter.Observer observer) {
        if (observer instanceof xowl.interpreter.observers.ClassObserver) {
            xowl.interpreter.observers.ClassObserver cObs = (xowl.interpreter.observers.ClassObserver)observer;
            if (!mapObsClass.containsKey(cObs.getObservedClass()))
                mapObsClass.put(cObs.getObservedClass(), new ArrayList<xowl.interpreter.observers.ClassObserver>());
            mapObsClass.get(cObs.getObservedClass()).add(cObs);
        } else if (observer instanceof xowl.interpreter.observers.ObjectPropertyObserver) {
            xowl.interpreter.observers.ObjectPropertyObserver pObs = (xowl.interpreter.observers.ObjectPropertyObserver)observer;
            if (!mapObsObjProp.containsKey(pObs.getObservedProperty()))
                mapObsObjProp.put(pObs.getObservedProperty(), new HashMap<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.ObjectPropertyObserver>>());
            Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.ObjectPropertyObserver>> map = mapObsObjProp.get(pObs.getObservedProperty());
            if (!map.containsKey(pObs.getObservedIndividual()))
                map.put(pObs.getObservedIndividual(), new ArrayList<xowl.interpreter.observers.ObjectPropertyObserver>());
            map.get(pObs.getObservedIndividual()).add(pObs);
        } else if (observer instanceof xowl.interpreter.observers.DataPropertyObserver) {
            xowl.interpreter.observers.DataPropertyObserver pObs = (xowl.interpreter.observers.DataPropertyObserver)observer;
            if (!mapObsDataProp.containsKey(pObs.getObservedProperty()))
                mapObsDataProp.put(pObs.getObservedProperty(), new HashMap<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.DataPropertyObserver>>());
            Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.DataPropertyObserver>> map = mapObsDataProp.get(pObs.getObservedProperty());
            if (!map.containsKey(pObs.getObservedIndividual()))
                map.put(pObs.getObservedIndividual(), new ArrayList<xowl.interpreter.observers.DataPropertyObserver>());
            map.get(pObs.getObservedIndividual()).add(pObs);
        }
    }
    @Override public void removeObserver(xowl.interpreter.Observer observer) {
        if (observer instanceof xowl.interpreter.observers.ClassObserver) {
            xowl.interpreter.observers.ClassObserver cObs = (xowl.interpreter.observers.ClassObserver)observer;
            if (!mapObsClass.containsKey(cObs.getObservedClass()))
                return;
            mapObsClass.get(cObs.getObservedClass()).remove(cObs);
        } else if (observer instanceof xowl.interpreter.observers.ObjectPropertyObserver) {
            xowl.interpreter.observers.ObjectPropertyObserver pObs = (xowl.interpreter.observers.ObjectPropertyObserver)observer;
            if (!mapObsObjProp.containsKey(pObs.getObservedProperty()))
                return;
            Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.ObjectPropertyObserver>> map = mapObsObjProp.get(pObs.getObservedProperty());
            if (!map.containsKey(pObs.getObservedIndividual()))
                return;
            map.get(pObs.getObservedIndividual()).remove(pObs);
        } else if (observer instanceof xowl.interpreter.observers.DataPropertyObserver) {
            xowl.interpreter.observers.DataPropertyObserver pObs = (xowl.interpreter.observers.DataPropertyObserver)observer;
            if (!mapObsDataProp.containsKey(pObs.getObservedProperty()))
                return;
            Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.DataPropertyObserver>> map = mapObsDataProp.get(pObs.getObservedProperty());
            if (!map.containsKey(pObs.getObservedIndividual()))
                return;
            map.get(pObs.getObservedIndividual()).remove(pObs);
        }
    }
    
    // Additional utility methods
    
    protected void applyAxiomFunctionDefinition(Evaluator evaluator, xowl.lang.actions.FunctionDefinition change, boolean isAdd) throws xowl.interpreter.XOWLException {
        xowl.lang.runtime.Function function = evaluator.evalFunction(evaluator, change.getFunction());
        xowl.lang.runtime.Closure exp = evaluator.evalClosure(evaluator, change.getBehavior());
        if (isAdd)
            addFunctionDefinition(function, exp);
        else
            removeFunctionDefinition(function, exp);
    }
    
    protected xowl.lang.runtime.Function getFunction(xowl.lang.actions.Lambda lambda) { return mapFunctions.get(lambda); }
    protected void addFunctionDefinition(xowl.lang.runtime.Function function, xowl.lang.runtime.Closure closure) {
        mapFunctions.put(closure.getCloses(), function);
        function.addDefinedAs(closure);
    }
    protected void removeFunctionDefinition(xowl.lang.runtime.Function function, xowl.lang.runtime.Closure closure) {
        mapFunctions.remove(closure.getCloses());
        for (xowl.lang.runtime.Closure tested : function.getAllDefinedAs()) {
            if (tested.getCloses() == closure.getCloses()) {
                function.removeDefinedAs(tested);
                break;
            }
        }
    }
    
    protected void addTransactionElement(xowl.interpreter.TransactionElement element) {
        if (transaction != null)
            transaction.addElement(element);
    }
    
    protected void onClassInstantiate(xowl.lang.runtime.Class _class, xowl.lang.runtime.Individual individual) {
        if (mapObsClass.containsKey(_class)) {
            List<xowl.interpreter.observers.ClassObserver> temp = new ArrayList<xowl.interpreter.observers.ClassObserver>(mapObsClass.get(_class));
            for (xowl.interpreter.observers.ClassObserver observer : temp)
                observer.onNewInstance(individual);
        }
    }
    protected void onClassDestroy(xowl.lang.runtime.Class _class, xowl.lang.runtime.Individual individual) {
        if (mapObsClass.containsKey(_class)) {
            List<xowl.interpreter.observers.ClassObserver> temp = new ArrayList<xowl.interpreter.observers.ClassObserver>(mapObsClass.get(_class));
            for (xowl.interpreter.observers.ClassObserver observer : temp)
                observer.onDestroyInstance(individual);
        }
    }
    protected void onPropertyAddValue(xowl.lang.runtime.Individual subject, xowl.lang.runtime.ObjectProperty property, xowl.lang.runtime.Individual value) {
        if (mapObsObjProp.containsKey(property)) {
            Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.ObjectPropertyObserver>> map = mapObsObjProp.get(property);
            if (map.containsKey(subject)) {
                List<xowl.interpreter.observers.ObjectPropertyObserver> temp = new ArrayList<xowl.interpreter.observers.ObjectPropertyObserver>(map.get(subject));
                for (xowl.interpreter.observers.ObjectPropertyObserver observer : temp)
                    observer.onAdd(subject, value);
            }
        }
    }
    protected void onPropertyRemoveValue(xowl.lang.runtime.Individual subject, xowl.lang.runtime.ObjectProperty property, xowl.lang.runtime.Individual value) {
        if (mapObsObjProp.containsKey(property)) {
            Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.ObjectPropertyObserver>> map = mapObsObjProp.get(property);
            if (map.containsKey(subject)) {
                List<xowl.interpreter.observers.ObjectPropertyObserver> temp = new ArrayList<xowl.interpreter.observers.ObjectPropertyObserver>(map.get(subject));
                for (xowl.interpreter.observers.ObjectPropertyObserver observer : temp)
                    observer.onRemove(subject, value);
            }
        }
    }
    protected void onPropertyAddValue(xowl.lang.runtime.Individual subject, xowl.lang.runtime.DataProperty property, xowl.lang.runtime.Literal value) {
        if (mapObsDataProp.containsKey(property)) {
            Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.DataPropertyObserver>> map = mapObsDataProp.get(property);
            if (map.containsKey(subject)) {
                List<xowl.interpreter.observers.DataPropertyObserver> temp = new ArrayList<xowl.interpreter.observers.DataPropertyObserver>(map.get(subject));
                for (xowl.interpreter.observers.DataPropertyObserver observer : temp)
                    observer.onAdd(subject, value);
            }
        }
    }
    protected void onPropertyRemoveValue(xowl.lang.runtime.Individual subject, xowl.lang.runtime.DataProperty property, xowl.lang.runtime.Literal value) {
        if (mapObsDataProp.containsKey(property)) {
            Map<xowl.lang.runtime.Individual, List<xowl.interpreter.observers.DataPropertyObserver>> map = mapObsDataProp.get(property);
            if (map.containsKey(subject)) {
                List<xowl.interpreter.observers.DataPropertyObserver> temp = new ArrayList<xowl.interpreter.observers.DataPropertyObserver>(map.get(subject));
                for (xowl.interpreter.observers.DataPropertyObserver observer : temp)
                    observer.onRemove(subject, value);
            }
        }
    }
    
    protected void loadResource(String resource, xowl.interpreter.OWL2Syntax syntax) {
        java.io.InputStream stream = null;
        try {
            stream = getResourceStream("xowl/ontologies/" + resource);
            loadOntology(resource, stream, syntax);
            stream.close();
        }
        catch (java.io.IOException ex) {
            log.error(comp, "Error while loading resource " + resource, ex);
        }
    }
    protected java.io.InputStream getResourceStream(String resource) throws java.io.IOException {
        java.lang.ClassLoader loader = java.lang.ClassLoader.getSystemClassLoader();
        return loader.getResourceAsStream(resource);
    }
}
